#pragma once
#include <string>
#include "looper.hpp"
#include "format.hpp"
#include "level.hpp"
#include "sink.hpp"
#include <unordered_map>
#include <mutex>
#include <cstdarg>
#include <vector>

namespace bit
{
    // 1.日志器的基类：
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;

    public:
        // 构造函数：
        // （1）用户如果提供自定义的模板格式走这里：
        Logger(const std::string &logname, const level::logLevel limitlevel,
               const std::vector<LogSink::ptr> &sinks, const std::string &pattern) : _logname(logname), _limitLevel(limitlevel), _fmt(pattern), _sinks(sinks.begin(), sinks.end()) {}
        // （2）用户如果不提供自定义的模板就走下面这个，使用Format的默认参数：
        Logger(const std::string &logname, const level::logLevel limitlevel,
               const std::vector<LogSink::ptr> &sinks) : _logname(logname), _limitLevel(limitlevel), _fmt(), _sinks(sinks.begin(), sinks.end()) {}

        // 功能一：获取日志器的名称：
        const std::string name()
        {
            return _logname;
        }
        // 功能二：核心功能，支持打印各个等级的日志信息：
        void debug(const std::string &file, size_t line, const char *pformat, ...)
        {
            va_list va;
            va_start(va, pformat);
            widget<level::logLevel::DEBUG>(file, line, pformat, va);
        }
        void info(const std::string &file, size_t line, const char *pformat, ...)
        {
            va_list va;
            va_start(va, pformat);
            widget<level::logLevel::INFO>(file, line, pformat, va);
        }
        void warn(const std::string &file, size_t line, const char *pformat, ...)
        {
            va_list va;
            va_start(va, pformat);
            widget<level::logLevel::WARN>(file, line, pformat, va);
        }
        void error(const std::string &file, size_t line, const char *pformat, ...)
        {
            va_list va;
            va_start(va, pformat);
            widget<level::logLevel::ERROR>(file, line, pformat, va);
        }
        void fatal(const std::string &file, size_t line, const char *pformat, ...)
        {
            va_list va;
            va_start(va, pformat);
            widget<level::logLevel::FATAL>(file, line, pformat, va);
        }
        //添加日志器的落地方向：
        template <typename Sink, class... Args>
        void addSink(Args... args)
        {
            LogSink::ptr psink = SinkFactory::create<Sink>(std::forward<Args>(args)...);
            _sinks.emplace_back(psink);
        }
    protected:
        // 这个函数模板需要通过显示实例化，将函数模板参数设置进来：
        template <level::logLevel Level>
        void widget(const std::string &file, size_t line, const char *pformat, va_list &va)
        {
            // 1.检查默认的日志输出等级，达到默认的日志输出等级，才进行日志的输出：
            if (_limitLevel > Level)
            {
                // 不能输出时，直接终止程序：
                return;
            }
            // 2.解析pformat的内容，用于初始化logMessage：
            char buff[1024];
            vsnprintf(buff, sizeof(buff), pformat, va);
            logMessage msg(file, line, _logname, Level, buff);

            va_end(va);
            // 3.格式化字符信息：
            std::string str = _fmt.format(msg);
            // 4.进行实际的日志落地：
            log(str, str.size());
        }
        virtual void log(const std::string &data, size_t len) = 0;

        // 非常重要：如果一个类，未来可能被继承，就需要将它的析构函数设置为虚函数，即便不存在需要显示析构的成员（多态的需要）：
        virtual ~Logger() {}

    protected:
        std::string _logname;
        level::logLevel _limitLevel; // 日志输出等级，低于这个等级的日志不被允许输出
        Format _fmt;
        std::mutex _mtx;                  // mutex可以不用显示初始化，它会自己调用自己的构造函数
        std::vector<LogSink::ptr> _sinks; // 日志器支持多落地模块，用数组维护这些模块
    };

    // 2.未来通过上面的日志器的基类——派生出“同步日志器”，“异步日志器”：
    // 2.1 同步日志器：
    class SyncLogger : public Logger
    {
    public:
        // 构造函数：
        SyncLogger(const std::string &logname, const level::logLevel limitlevel,
                   const std::vector<LogSink::ptr> &sinks, const std::string &pattern)
            : Logger(logname, limitlevel, sinks, pattern) {}
        SyncLogger(const std::string &logname, const level::logLevel limitlevel,
                   const std::vector<LogSink::ptr> &sinks)
            : Logger(logname, limitlevel, sinks) {}

    private:
        void log(const std::string &data, size_t len) override
        {
            // 为了保证线程安全，所以必须加锁：
            std::lock_guard<std::mutex> lock(_mtx);
            for (auto &sink : _sinks)
            {
                sink->log(data.c_str(), len);
            }
        }
    };
    // 2.2 异步日志器：
    class AsyncLogger : public Logger
    {
    public:
        // 构造函数：
        AsyncLogger(const std::string &logname, const level::logLevel limitlevel,
                    const std::vector<LogSink::ptr> &sinks,
                    AsyncLooper::LoopType looptype,
                    const std::string &pattern) : Logger(logname, limitlevel, sinks, pattern), _looper(std::bind(&AsyncLogger::callback, this, std::placeholders::_1), looptype) {}
        AsyncLogger(const std::string &logname, const level::logLevel limitlevel,
                    const std::vector<LogSink::ptr> &sinks,
                    AsyncLooper::LoopType looptype = AsyncLooper::LoopType::SAFE) : Logger(logname, limitlevel, sinks), _looper(std::bind(&AsyncLogger::callback, this, std::placeholders::_1), looptype) {}

    private:
        void log(const std::string &data, size_t len) override
        {
            // 不需要加锁，因为_looper在设计这个接口的时候本身就是线程安全的：
            _looper.push(data.c_str(), len);
        }
        // 回调函数的设计：
        void callback(Buffer &buff)
        {
            // 因为异步工作器里面的工作线程只有一个，所以不加锁也没关系：
            for (auto &sink : _sinks)
            {
                sink->log(buff.begin(), buff.readAbleSize());
            }
        }

    private:
        AsyncLooper _looper;
    };

    // 抽象一个日志器的建造者类：
    class LogBuilder
    {
    public:
        enum class LogType
        {
            SYNC, // 这是同步日志器
            ASYNC // 这是异步日志器
        };

    public:
        // 构造函数：
        LogBuilder(const std::string &logname, const LogType &logtype, const level::logLevel &limitLevel, const AsyncLooper::LoopType &looptype)
            : _logname(logname), _limitLevel(limitLevel), _logtype(logtype), _looptype(looptype) {}

        // 各种build方法:
        void buildLogName(const std::string &logname) { _logname = logname; }
        void buildLogType(const LogType &logtype) { _logtype = logtype; }
        void buildLogLevel(level::logLevel limitLevel) { _limitLevel = limitLevel; }
        void buildLogFormat(const std::string &pattern) { _fmt = Format(pattern); }
        void buildLoopType(const AsyncLooper::LoopType &looptype) { _looptype = looptype; }
        template <typename Sink, class... Args>
        void buildSink(Args... args)
        {
            LogSink::ptr psink = SinkFactory::create<Sink>(std::forward<Args>(args)...);
            _sinks.emplace_back(psink);
        }
        virtual Logger::ptr build() = 0;

    protected:
        LogType _logtype;            // 日志器的类型
        std::string _logname;        // 日志器的名称
        level::logLevel _limitLevel; // 日志的限制输出级别
        Format _fmt;                 // 日志的格式
        AsyncLooper::LoopType _looptype;
        std::vector<LogSink::ptr> _sinks;
    };
    // 通过这个类派生出“局部日志器” + “全局日志器”：
    // 1.局部日志器：
    class LocalLogBuilder : public LogBuilder
    {
    public:
        // 构造函数：
        LocalLogBuilder(const std::string &logname, const level::logLevel &limitLevel = level::logLevel::DEBUG, const LogType &logtype = LogType::SYNC, const AsyncLooper::LoopType &looptype = AsyncLooper::LoopType::SAFE)
            : LogBuilder(logname, logtype, limitLevel, looptype) {}
        // 重写build方法：
        Logger::ptr build() override
        {
            Logger::ptr ret = nullptr;
            // 如果日志落地方向为空，就给一个“标准输出”作为默认的落地方向：
            if (_sinks.empty())
            {
                buildSink<StdoutSink>();
            }
            if (_logtype == LogType::SYNC)
            {
                ret = std::shared_ptr<Logger>(new SyncLogger(_logname, _limitLevel, _sinks, _fmt.pattern()));
            }
            else
                ret = std::make_shared<AsyncLogger>(_logname, _limitLevel, _sinks, _looptype, _fmt.pattern());
            return ret;
        }
    };

    // 2.全局日志器：
    //  给日志设计一个单例管理：
    class LogAdministrator
    {
    private:
        // 1.构造函数私有化：
        LogAdministrator()
        {
            // 要完成对默认的日志器的初始化：
            LocalLogBuilder builder("root");
            _rootLog = builder.build();
            _loggers.insert(std::make_pair("root", _rootLog));
        }

        // 2.禁用拷贝构造和赋值：
        LogAdministrator(const LogAdministrator &tmp) = delete;
        LogAdministrator &operator=(const LogAdministrator &tmp) = delete;

        // 3.析构函数也私有：
        ~LogAdministrator() = default;

    public:
        // 获取单例对象：注意，由于构造函数被私有化了，所以这个函数就只能设置为类函数——static的成员函数
        static LogAdministrator &getInstance()
        {
            // C++语言本身的特性（C++ 11这种操作是线程安全的）保证了这个对象只会被实例化一份:
            static LogAdministrator eton;
            return eton;
        }

        // 添加新的日志器：
        bool addLogger(Logger::ptr log)
        {
            // 做个判断
            if (logExists(log->name()))
                return false;

            // 要加锁，保证添加日志器的过程是线程安全的：
            std::lock_guard<std::mutex> lock(_mtx);
            _loggers.insert({log->name(), log});
            return true;
        }

        // 获取日志器：给定日志器的名称，返回咋hiding日志器的ptr：
        Logger::ptr getLogger(const std::string name)
        {
            if (!logExists(name))
                return nullptr;

            std::lock_guard<std::mutex> lock(_mtx);
            return _loggers[name];
        }

        // 获取默认的日志器：
        Logger::ptr rootLogger()
        {
            return _rootLog;
        }

    private:
        // 给定日志器的名称，判断日志器是否存在：
        bool logExists(const std::string name)
        {
            bool ret = true;
            // 要加锁，保证有线程在查找过程中，没有其他的线程来添加日志器，从而带来数据的访问不一致问题：
            std::lock_guard<std::mutex> lock(_mtx);
            // 这个函数会根据用户给定的key值，在哈希表中查找和key值一致的值，找到了返回对应位置的迭代器，反之返回end()：
            std::unordered_map<std::string, Logger::ptr>::iterator ip = _loggers.find(name);
            if (ip == _loggers.end())
            {
                ret = false;
            }
            return ret;
        }

    private:
        std::mutex _mtx;      // 添加新的日志器时，多执行流需要加锁；
        Logger::ptr _rootLog; // 默认的日志器；

        // 用KV哈希表来管理这些日志器：
        std::unordered_map<std::string, Logger::ptr> _loggers;
    };

    class GlobalLogBuilder : public LogBuilder
    {
    public:
        // 构造函数：
        GlobalLogBuilder(const std::string &logname, const level::logLevel &limitLevel = level::logLevel::DEBUG, const LogType &logtype = LogType::SYNC, const AsyncLooper::LoopType &looptype = AsyncLooper::LoopType::SAFE)
            : LogBuilder(logname, logtype, limitLevel, looptype) {}
        // 重写build方法：
        Logger::ptr build() override
        {
            Logger::ptr ret = nullptr;
            // 如果日志落地方向为空，就给一个“标准输出”作为默认的落地方向：
            if (_sinks.empty())
            {
                buildSink<StdoutSink>();
            }
            if (_logtype == LogType::SYNC)
            {
                ret = std::shared_ptr<Logger>(new SyncLogger(_logname, _limitLevel, _sinks, _fmt.pattern()));
            }
            else
                ret = std::make_shared<AsyncLogger>(_logname, _limitLevel, _sinks, _looptype, _fmt.pattern());
            LogAdministrator::getInstance().addLogger(ret);
            return ret;
        }
    };
}